iT邦幫忙

2024 iThome 鐵人賽

DAY 2
0

Summarize

  • struct file, defined in <include/linux/fs.h>, is the second most important data structure
    used in device drivers. Note that a file has nothing to do with the FILE pointers of
    user-space programs
  • The file structure represents an open file. (It is not specific to device drivers; every
    open file in the system has an associated struct file in kernel space.)
  • The inode structure is used by the kernel internally to represent files. Therefore, it is
    different from the file structure that represents an open file descriptor

Outline

  • The file Structure
  • The inode Structure
  • 補充: file handle
  • 補充: file descriptor

The file Structure

struct file, defined in <include/linux/fs.h>, is the second most important data structure
used in device drivers. Note that a file has nothing to do with the FILE pointers of
user-space programs

struct file 代表了一個已開啟的文件,它是一個內核空間的結構,與用戶空間的 FILE 完全無關。該結構主要用於管理文件的開啟和操作

  • f_mode: 指示文件的讀寫模式(如 FMODE_READ, FMODE_WRITE),可以用來檢查文件的讀寫權限
  • f_pos: 當前的讀寫位置。這是一個 64 位的 loff_t 類型,應在驅動程式的 read 和 write 操作中使用該值進行數據讀取或寫入,而不是直接修改它
  • f_flags: 文件的操作標誌,如 O_RDONLY(只讀),O_NONBLOCK(非阻塞操作),O_SYNC(同步 I/O)。其中 O_NONBLOCK 標誌在驅動程式中經常用來實現非阻塞 I/O 操作
  • f_op: 文件的操作集(struct file_operations)。該指標在打開文件時被內核設置,用來分發所有針對該文件的操作。可以在文件開啟過程中根據不同的條件替換該結構
  • private_data: 一個由驅動程式自由使用的指針。該指針在文件打開時被設為 NULL,可以在需要跨系統調用保存狀態信息時使用。通常在 release 方法中需要釋放與之關聯的資源
  • f_dentry: 關聯的目錄條目(struct dentry)。通常驅動程式不需要處理 dentry 結構,但可以通過 filp->f_dentry->d_inode 訪問 inode 結構。

Example (include/linux/fs.h): Linux-6.6

/*
 * f_{lock,count,pos_lock} members can be highly contended and share
 * the same cacheline. f_{lock,mode} are very frequently used together
 * and so share the same cacheline as well. The read-mostly
 * f_{path,inode,op} are kept on a separate cacheline.
 */
struct file {
	union {
		struct llist_node	f_llist;
		struct rcu_head 	f_rcuhead;
		unsigned int 		f_iocb_flags;
	};

	/*
	 * Protects f_ep, f_flags.
	 * Must not be taken from IRQ context.
	 */
	spinlock_t		f_lock;
	fmode_t			f_mode;
	atomic_long_t		f_count;
	struct mutex		f_pos_lock;
	loff_t			f_pos;
	unsigned int		f_flags;
	struct fown_struct	f_owner;
	const struct cred	*f_cred;
	struct file_ra_state	f_ra;
	struct path		f_path;
	struct inode		*f_inode;	/* cached value */
	const struct file_operations	*f_op;

	u64			f_version;
#ifdef CONFIG_SECURITY
	void			*f_security;
#endif
	/* needed for tty driver, and maybe others */
	void			*private_data;

#ifdef CONFIG_EPOLL
	/* Used by fs/eventpoll.c to link all the hooks to this file */
	struct hlist_head	*f_ep;
#endif /* #ifdef CONFIG_EPOLL */
	struct address_space	*f_mapping;
	errseq_t		f_wb_err;
	errseq_t		f_sb_err; /* for syncfs */
} __randomize_layout
  __attribute__((aligned(4)));	/* lest something weird decides that 2 is OK */

補充: file_handle
file_handle 是 Linux 中的結構體,常用於系統調用(例如 name_to_handle_at() 和 open_by_handle_at())

  • 這個結構體並不是直接用來表示文件描述符(file descriptor, FD),而是一種提供給使用者空間的文件識別方式
struct file_handle {
	__u32 handle_bytes;
    
    // 這通常用來標識句柄所屬的文件系統類型。不同的文件系統可能會定義不同類型的句柄以便能正確識別文件
	int handle_type;    
    
	/* file identifier */
    // flexible array member,其大小由 handle_bytes 指定。它實際上存放了文件的識別符。不同文件系統可能會將不同的資訊存儲在這裡,具體取決於文件系統的實現
    unsigned char f_handle[] __counted_by(handle_bytes);   
};

fd 與 fhandle 的主要區別在於使用場景:

  • File descriptor 是進程專用,並且只在文件打開期間有效
  • File handle 是跨進程或跨系統的,適合在進程之間或不同時間點使用文件的標識符

The inode Structure

The inode structure is used by the kernel internally to represent files. Therefore, it is
different from the file structure that represents an open file descriptor

struct inode 是用來代表文件的數據結構,與文件描述符(file descriptor)無關。每個文件在內核中對應一個 inode,而每個 struct file 都會指向相應的 inode

  • i_rdev: 當 inode 代表一個設備文件時,這個字段包含實際的設備號。該字段提供文件對應的設備信息
  • i_cdev: 指向 struct cdev,這是內核用來表示字元設備的內部結構。當 inode 與字元設備關聯時,這個字段會保存對應的 cdev 結構
  • *iminor(struct inode inode) 和 imajor(struct inode *inode): 用來從 inode 中獲取次要和主要設備號的宏。直接操作 i_rdev 可能導致程式在內核版本升級後無法兼容,因此應該使用這些宏來獲取設備號

簡化版範例 (include/linux/fs.h): Linux-6.6

struct inode {
    umode_t         i_mode;         /* 文件類型和權限 */
    kuid_t          i_uid;          /* 文件擁有者的用戶 ID */
    kgid_t          i_gid;          /* 文件擁有者的組 ID */
    unsigned long   i_ino;          /* inode 編號 */
    dev_t           i_rdev;         /* 設備編號(特別對於設備文件) */
    loff_t          i_size;         /* 文件大小(以字節為單位) */
    struct timespec64 i_atime;      /* 最後一次訪問時間 */
    struct timespec64 i_mtime;      /* 最後一次修改時間 */
    struct timespec64 i_ctime;      /* inode 最後修改時間(狀態變更時間) */
    unsigned int    i_nlink;        /* 硬連接數 */
    blkcnt_t        i_blocks;       /* 文件所佔用的區塊數 */
    struct super_block *i_sb;       /* 指向文件系統的 super block */
    const struct inode_operations *i_op; /* inode 操作方法 */
    const struct file_operations *i_fop; /* 文件操作方法 */
    struct address_space *i_mapping; /* 文件內容的映射 */
    atomic_t        i_count;        /* inode 引用計數 */
};

補充: file descriptor

路徑: include/linux/fdtable.h

struct fdtable {
	unsigned int max_fds;
	struct file __rcu **fd;      /* current fd array */
	unsigned long *close_on_exec;
	unsigned long *open_fds;
	unsigned long *full_fds_bits;
	struct rcu_head rcu;
};


/*
 * Open file table structure
 */
struct files_struct {
  /*
   * read mostly part
   */
	atomic_t count;
	bool resize_in_progress;
	wait_queue_head_t resize_wait;

	struct fdtable __rcu *fdt;
	struct fdtable fdtab;
  /*
   * written part on a separate cache line in SMP
   */
	spinlock_t file_lock ____cacheline_aligned_in_smp;
	unsigned int next_fd;
	unsigned long close_on_exec_init[1];
	unsigned long open_fds_init[1];
	unsigned long full_fds_bits_init[1];
	struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};

每個 Process 都有一個 struct files_struct,其中包含該進程開啟的所有文件的資訊

  • 在 files_struct 中,有一個指向 struct fdtable 的指標 fdt。這個 fdtable 包含一個 file** 的指標陣列,這個陣列中每個元素對應一個文件描述符,並指向核心中代表該文件的 struct file 結構
  • 當進程打開一個新文件時,系統會在 fdtable 中分配一個新的文件描述符,並將該文件描述符對應的 fdtable->fd[] 更新為指向新開啟文件的 struct file
進程 (process)
  |
  |---> struct files_struct
           |
           |---> struct fdtable
                    |
                    |---> fd[0] --> struct file (代表標準輸入)
                    |
                    |---> fd[1] --> struct file (代表標準輸出)
                    |
                    |---> fd[2] --> struct file (代表標準錯誤)

小結

  • 在 Linux 核心中,struct file 主要管理打開的文件,並且針對每次系統調用都會傳遞該結構,通常用來管理文件的 I/O 操作
  • struct inode 代表文件本身,包含文件的元數據信息和設備文件的具體設備號,因此它更像是文件的靜態描述,而 struct file 是對打開文件的動態管理

Reference

  1. 奔跑吧 CH 3.1 進程的誕生
  2. struct file结构体

上一篇
[Day04] Chapter 3: Char Drivers (1)
系列文
LDD3 (Linux Device Drivers, 3th) 學習筆記5
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言